package automaton;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;

import automaton.efsm.DefiniteEFSM;
import automaton.efsm.NondefiniteEFSM;

/**
 * 
 * @author Daniil Chivilikhin
 * Class for storing a DFA with metadata corresponding to 
 * the calculation of the fitness function of the DFA.
 */
public class AutomatonMetaData {
	private DefiniteEFSM dfa;
	private ArrayList<NondefiniteEFSM.Transition> visitedTransitions;
	private ArrayList<AutomatonMetaData> bestAutomatasHistory;
	private int numberOfSteps;
	public double time;
	private double fitness;
	
	public AutomatonMetaData(DefiniteEFSM dfa, ArrayList<NondefiniteEFSM.Transition> visitedTransitions, double fitness) {
		this.dfa = dfa;
		this.visitedTransitions = visitedTransitions;
		this.fitness = fitness;
		this.numberOfSteps = 0;
		this.bestAutomatasHistory = new ArrayList<AutomatonMetaData>();
	}
	
	public void setNumberOfSteps(int numberOfSteps) {
		this.numberOfSteps = numberOfSteps;
	}
	
	public void setTime(double time) {
		this.time = time;
	}
	
	public DefiniteEFSM getDFA() {
		return dfa;
	}
	
	public double getFitness() {
		return fitness;
	}
	
	public void setFitness(double fitness ) {
		this.fitness = fitness;
	}
	
	public ArrayList<NondefiniteEFSM.Transition> getVisitedTransitions() {
		return visitedTransitions;
	}

	public int getNumberOfSteps() {
		return numberOfSteps;
	}
	
	public void setFitnessHistory(ArrayList<AutomatonMetaData> history) {
		this.bestAutomatasHistory = (ArrayList<AutomatonMetaData>) history.clone();
	}
	
	public int getNumberOfVisitedStates() {
		int result = 0;
		boolean visited[] = new boolean[dfa.getNumberOfStates()];
		Arrays.fill(visited, false);
		
		for (NondefiniteEFSM.Transition tr : visitedTransitions) {
			visited[tr.getStartState()] = true;
			visited[tr.getEndState()] = true;
		}
		
		for (int i = 0; i < visited.length; i++) {
			if (visited[i]) {
				result += 1;
			}
		}
		return result;
	}
	
	public void printTransitionDiagram(String dirname) {
		PrintWriter out = null;
		try {
			out = new PrintWriter(new File(dirname + "/" + fitness + "_transitions"));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		out.print(dfa);
		out.close();
	}
	
	public void printToGraphViz(String dirname, ArrayList<String> events) {
		PrintWriter out = null;
		try {
			out = new PrintWriter(new File(dirname + "/" + fitness + ".gv"));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}

		out.println("digraph dfa{");
		out.println("rankdir = LR");

		for (int i = 0; i < dfa.getNumberOfStates(); i++) {
			out.println("\"" + (i + 1) + "\"" + "[label = \"" + (i + 1)
					+ "\"];");
		}

		for (int i = 0; i < dfa.getNumberOfStates(); i++) {
			for (int j = 0; j < dfa.getNumberOfExternalInfluences(); j++) {
				if (dfa.transitions[i][j].getEndState() == -1) {
					continue;
				}

				ArrayList<String> actions = dfa.transitions[i][j].getActions();
				String actionsToPrint = "";
				for (String s : actions) {
					if (s.equals("zn")) {
						s = "";
					}
				}
				
				for (int k = 0; k < actions.size() - 1; k++) {
					if (!actions.get(k).equals("zn")) {
						actionsToPrint += actions.get(k) + ",";
					}
				}
				if (!actions.get(actions.size() - 1).equals("zn")) {
					actionsToPrint += actions.get(actions.size() - 1);
				}
				
				for (NondefiniteEFSM.Transition tr : visitedTransitions) {
					if (tr.getStartState() == i && tr.getSymbol() == j
							&& tr.getEndState() == dfa.transitions[i][j].getEndState()) {
						out.println("\"" + (i + 1) + "\" -> \""
								+ (dfa.transitions[i][j].getEndState() + 1)
								+ "\" [label = \"" + events.get(j) + "/" + actionsToPrint
								+ "\"];");
						break;
					}
				}
			}
		}

		out.print("}");
		out.close();
	}
	
	public void printMetaData(String dirname) {
		PrintWriter out = null;
		try {
			out = new PrintWriter(new File(dirname + "/" + fitness + "_metadata"));
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
		
		out.println("fitness = " + fitness);
		out.println("step-count = " + numberOfSteps);
		out.println("time = " + time);
		for (AutomatonMetaData data : bestAutomatasHistory) {
			out.println(data.numberOfSteps + " " + data.fitness);
		}
		
		out.close();
	}
}
